package com.atguigu.gmall.gateway.filter;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.gateway.properties.AuthUrlProperties;
import com.atguigu.gmall.user.entity.UserInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.net.URI;
import java.nio.charset.StandardCharsets;

/**
 * @author lhh
 * @ClassName UserAuthFilter
 * @date 2022 2022/12/15
 * id透传
 */

@Slf4j
@Component
public class UserAuthFilter implements GlobalFilter {

    @Resource
    AuthUrlProperties authUrlProperties;

    @Autowired
    StringRedisTemplate redisTemplate;

    AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String path = exchange.getRequest().getURI().getPath();
        log.info("请求开始：{}", path);
        //如果是静态资源，直接放行无需查询用户身份信息
        //拼配静态资源
        long count = authUrlProperties.getAnyoneUrl()
                .stream()
                .filter(pattern ->
                        antPathMatcher.match(pattern, path)).count();
        if (count > 0) {
            log.info("静态资源直接放行");
            return chain.filter(exchange);
        }

        //内部路径直接拒绝
        long denyCount = authUrlProperties.getDenyUrl()
                .stream().
                filter(pattern ->
                        antPathMatcher.match(pattern, path)).count();
        if (denyCount > 0) {
            log.warn("内部路径禁止访问。。。");
            //返回错误json
            Result<String> build = Result.build("", ResultCodeEnum.PERMISSION);
            return responseJson(exchange, build);
        }
        //有限权限访问
        long AuthCount = authUrlProperties.getAuthUrl()
                .stream()
                .filter(pattern -> antPathMatcher.match(pattern, path))
                .count();

        if (AuthCount > 0) {
            //必须登录才能访问
            log.info("验证用户信息开始。。。。");
            //获取请求头或者cookie中携带的数据
            String token = getToken(exchange);
            //根据获取的token，去查询缓存中的userInfo
            UserInfo userInfo = getUserInfo(token);
            if (StringUtils.isEmpty(token) || userInfo == null) {
                //重定向登录页面
                return redirectToPage(exchange, authUrlProperties.getLoginPage());
            }
        }

        log.info("验证用户信息开始。。。。");
        UserInfo userInfo = getUserInfo(getToken(exchange));

        return userIdThtougth(exchange, userInfo, chain);

        //filter放行  非阻塞
//        Mono<Void> filter = chain
//                .filter(exchange)
//                .doFinally((item) -> {
//                    //log.info("请求结束:{}",request.getURI().toString());
//                });
//        ;
//        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
//        });
//        return filter;
    }

    private Mono<Void> userIdThtougth(ServerWebExchange exchange, UserInfo userInfo, GatewayFilterChain chain) {
        ServerHttpRequest.Builder builder = exchange.getRequest().mutate();
        if (userInfo != null) {
            log.info("用户信息放在头中，往下透传");
            builder.header(RedisConst.USER_ID_HEADER, userInfo.getId().toString()).build();
        }
        //3、临时id
        String tempId = getTempId(exchange);
        if (!StringUtils.isEmpty(tempId)) {
            log.info("用户临时信息放在头中，往下透传");
            //只要构造完成，老的exchange里面的request也会跟着变
            builder.header(RedisConst.TEMP_ID_HEADER, tempId)
                    .build();
        }
        //放行
        return chain.filter(exchange);
    }

    private String getTempId(ServerWebExchange exchange) {
        ServerHttpRequest request = exchange.getRequest();
        String token = request.getHeaders().getFirst("userTempId");
        if (!StringUtils.isEmpty(token)) {
            return token;
        }
        //cookie中获取
        HttpCookie first = request.getCookies().getFirst("userTempId");
        if (first != null) {
            return first.getValue();

        }
        return null;
    }

    /**
     * 重定向到登录页面
     *
     * @param exchange
     * @param loginPage
     * @return
     */
    private Mono<Void> redirectToPage(ServerWebExchange exchange, String loginPage) {
        ServerHttpResponse response = exchange.getResponse();
        //当时的请求路径
        URI uri = exchange.getRequest().getURI();
        //跳转路径
        loginPage += "?originUrl=" + uri.toString();

        //设置响应码
        response.setRawStatusCode(HttpStatus.FOUND.value());

        //设置响应头
        response.getHeaders().set("Location", loginPage);
        return response.setComplete();
    }

    private Mono<Void> responseJson(ServerWebExchange exchange, Result<String> build) {
        //得到响应数据
        ServerHttpResponse response = exchange.getResponse();
        //得到s数据
        String json = JSON.toJSONString(build);
        DataBuffer dataBuffer = response.bufferFactory()
                .wrap(json.getBytes(StandardCharsets.UTF_8));
        //响应格式
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF8);
        Mono<Void> voidMono = response.writeWith(Mono.just(dataBuffer));
        return voidMono;
    }

    /**
     * 查询缓存中的用户信息
     */
    private UserInfo getUserInfo(String token) {
        String json = redisTemplate.opsForValue().get("user:login:" + token);
        if (!StringUtils.isEmpty(json)) {
            UserInfo userInfo = JSON.parseObject(json, UserInfo.class);
            return userInfo;
        }
        return null;
    }

    /**
     * 获取请求头或者cookie中携带的用户令牌标识
     *
     * @param exchange
     * @return
     */
    private String getToken(ServerWebExchange exchange) {
        //1、拿到请求
        ServerHttpRequest request = exchange.getRequest();
        //2、先看请求头
        String token = request.getHeaders().getFirst("token");
        if (!StringUtils.isEmpty(token)) {
            return token;
        }

        //3、如果头没有，就看cookie； 有可能没登录这个cookie都没有
        HttpCookie first = request.getCookies().getFirst("token");
        if (first != null) {
            return first.getValue();
        }
        return null;
    }

}
